package org.budo.time.impl;

import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;

import org.budo.time.Time;
import org.budo.time.TimePoint;
import org.budo.time.TimeSpan;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * 表示一个时间段
 * 
 * @author limingwei
 */
@Slf4j
@Getter
@Setter
@NoArgsConstructor
public class BudoTimeSpan implements TimeSpan, Serializable {
    private static final long serialVersionUID = 4355045459032295931L;

    /**
     * 开始
     */
    private TimePoint from;

    /**
     * 结束
     */
    private TimePoint to;

    /**
     * 长度
     */
    private Long milliSeconds;

    /**
     * 制定时间段长度
     */
    public BudoTimeSpan(Long milliSeconds) {
        this.milliSeconds = milliSeconds;
    }

    /**
     * 指定时间段起止时间
     */
    public BudoTimeSpan(TimePoint from, TimePoint to) {
        this.from = from;
        this.to = to;
    }

    /**
     * 指定时间段起止时间
     */
    public BudoTimeSpan(Date from, Date to) {
        this(Time.when(from), Time.when(to));
    }

    /**
     * 取正
     */
    @Override
    public TimeSpan abs() {
        if (null == this.from && null == this.to && null == this.milliSeconds) { // 都空
            return this;
        }

        Long _milliSeconds = this.toMilliSeconds();
        if (null != _milliSeconds && _milliSeconds >= 0) {
            return this; // 本来就大于0
        }

        // 时间段形式
        if (null != this.getFrom() && null != this.getTo()) {
            return new BudoTimeSpan(this.getTo(), this.getFrom());
        }

        return new BudoTimeSpan(0 - this.getMilliSeconds());
    }

    /**
     * 时间段转换为毫秒数
     */
    public Long toMilliSeconds() {
        if (null != this.milliSeconds) {
            return this.milliSeconds;
        }

        synchronized (this) {
            if (null != this.milliSeconds) {
                return this.milliSeconds;
            }

            if (null != this.from && null != this.to) {
                Long fromMilliSec = from.toMilliSeconds();
                Long toMilliSec = to.toMilliSeconds();
                if (null != fromMilliSec && null != toMilliSec) {
                    return this.milliSeconds = toMilliSec - fromMilliSec;
                }
            }

            log.error("#99 this=" + this);
            return null;
        }

    }

    /**
     * 转为秒数 ，可用 intValue() 舍弃余量
     */
    public Double toSeconds() {
        Long _milliSeconds = this.toMilliSeconds();
        if (null == _milliSeconds) {
            return null;
        }

        return _milliSeconds / 1000.00;
    }

    @Override
    public Double toSeconds(Number defaultValue) {
        Double _seconds = this.toSeconds();
        return null != _seconds ? _seconds : defaultValue.doubleValue();
    }

    /**
     * 转为分钟数 ，可用 intValue() 舍弃余量
     */
    public Double toMinutes() {
        if (null == this.toMilliSeconds()) {
            return null;
        }

        return this.toSeconds() / 60;
    }

    /**
     * 转为小时数 ，可用 intValue() 舍弃余量
     */
    public Double toHours() {
        if (null == this.toMilliSeconds()) {
            return null;
        }

        return this.toMinutes() / 60;
    }

    /**
     * 转为天数，可用 intValue() 舍弃余量
     */
    public Double toDays() {
        if (null == this.toMilliSeconds()) {
            return null;
        }

        return this.toHours() / 24;
    }

    @Override
    public Double toMonths() {
        if (null == this.toMilliSeconds()) {
            return null;
        }

        return this.toDays() / 30;
    }

    @Override
    public Double toYears() {
        if (null == this.toMilliSeconds()) {
            return null;
        }

        return this.toMonths() / 12;
    }

    @Override
    public Duration toDuration() {
        if (null != this.from && null != this.to) {
            Instant _from = Time.when(this.from).toInstant();
            Instant _to = Time.when(this.to).toInstant();
            return Duration.between(_from, _to);
        }

        if (null != this.toMilliSeconds()) {
            return Duration.ofMillis(this.toMilliSeconds());
        }

        return null;
    }

    @Override
    public Integer toMinutesCeil() {
        return (int) Math.ceil(this.toMinutes());
    }

    @Override
    public Integer toMinutesRound() {
        return (int) Math.round(this.toMinutes());
    }

    @Override
    public Integer toMinutesFloor() {
        return (int) Math.floor(this.toMinutes());
    }

    public String toString() {
        String str = "";

        if (null == this.milliSeconds) {
            str += "from=" + this.from;
            str += ", to=" + this.to;
        } else {
            str += this.milliSeconds + "ms";
        }

        if (null != this.milliSeconds) {
            str += ", " + this.toDays().intValue() + "d ";
            str += (this.toHours().intValue() - (24 * this.toDays().intValue())) + "h ";
            str += (this.toMinutes().intValue() - (60 * this.toHours().intValue())) + "m ";
            str += (this.toSeconds().intValue() - (60 * this.toMinutes().intValue())) + "s ";
            str += (this.toMilliSeconds().intValue() - (1000 * this.toSeconds().intValue())) + "ms";
        }

        return str;
    }
}
